home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / nxyplot1.87 / PlotView.m < prev    next >
Encoding:
Text File  |  1992-08-04  |  53.2 KB  |  1,707 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "defs.h"
  5. #import "PlotView.h"
  6. #import "Plot.h"
  7. #import <appkit/SavePanel.h>
  8. #import <appkit/color.h>
  9. #import <appkit/FormCell.h>
  10. #import <appkit/PrintPanel.h>
  11. #import <appkit/Pasteboard.h>
  12.  
  13. /* The following routines are in auxil.m: */
  14. extern void count_labels(int *, double *, double, double, double);
  15. extern void autoformat(double, double, double, int *);
  16. extern void handformat(float, char *, int *);
  17.  
  18. @implementation PlotView
  19.  
  20. - (BOOL) acceptsFirstMouse { return YES;}      /* grab that mouse down event! */
  21.  
  22. - provideMainTitleFont   { return newMainTitleFont;}
  23. - provideXTitleFont      { return newXTitleFont;}
  24. - provideYTitleFont      { return newYTitleFont;}
  25. - provideLegendTitleFont { return newLegendTitleFont;}
  26. - provideLegendFont      { return newLegendFont;}
  27. - provideTicLabelFont    { return newTicLabelFont;}
  28. - (NXPoint) provideLegendBoxOrigin { return legendbox.origin;}
  29. - (NXPoint) provideXTitleBoxOrigin { return xtitlebox.origin;}
  30. - (NXPoint) provideYTitleBoxOrigin { return ytitlebox.origin;}
  31. - (NXPoint) provideMainTitleBoxOrigin { return maintitlebox.origin;}
  32.  
  33. - provideWindowFrame:(NXRect *)windowframe
  34. {
  35.   return [[self window] getFrame:windowframe];
  36. }
  37.  
  38. /* All these method names that start with "force" would more naturally start
  39.  * with "set", but everything malfunctions then; apparently it's a bad idea
  40.  * to have methods whose names begin with "set".  Is it also a bad idea to
  41.  * have methods whose names begin with "init"?
  42.  */
  43. - forceWindowFrame:(NXRect *)windowframe
  44. {
  45.   [[self window] placeWindow:windowframe];
  46.   oldbounds = bounds;        /* have to do this right here so that the */
  47.   [self display];        /* title boxes are not moved around again */
  48.   return self;
  49. }
  50.  
  51. - forceLegendBoxOrigin: (NXPoint)point
  52. {
  53.   legendbox.origin = point;
  54.   return self;
  55. }
  56.  
  57. - forceXTitleBoxOrigin: (NXPoint)point
  58. {
  59.   xtitlebox.origin = point;
  60.   return self;
  61. }
  62.  
  63. - forceYTitleBoxOrigin: (NXPoint)point
  64. {
  65.   ytitlebox.origin = point;
  66.   return self;
  67. }
  68.  
  69. - forceMainTitleBoxOrigin: (NXPoint)point
  70. {
  71.   maintitlebox.origin = point;
  72.   return self;
  73. }
  74.  
  75. - forceMainTitleFont:(char *)fontname :(float)fontsize
  76. {
  77.   newMainTitleFont =
  78.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  79.   return self;
  80. }
  81.  
  82. - forceXTitleFont:(char *)fontname :(float)fontsize;
  83. {
  84.   newXTitleFont =
  85.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  86.   return self;
  87. }
  88.  
  89. - forceYTitleFont:(char *)fontname :(float)fontsize;
  90. {
  91.   newYTitleFont =
  92.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  93.   return self;
  94. }
  95.  
  96. - forceLegendFont:(char *)fontname :(float)fontsize;
  97. {
  98.   newLegendFont =
  99.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  100.   return self;
  101. }
  102.  
  103. - forceLegendTitleFont:(char *)fontname :(float)fontsize;
  104. {
  105.   newLegendTitleFont =
  106.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  107.   return self;
  108. }
  109.  
  110. - forceTicLabelFont:(char *)fontname :(float)fontsize;
  111. {
  112.   newTicLabelFont =
  113.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  114.   return self;
  115. }
  116.  
  117. - initFrame:(const NXRect *)frameRect
  118. {
  119.   [super initFrame:frameRect];
  120.   legendbox.origin.x = XOFFSET + 50.0; /* get the legendbox initialized here */
  121.   legendbox.origin.y = YOFFSET + 50.0;
  122.   xtitlebox.origin.x = XOFFSET/4.0
  123.                    + (bounds.size.width - DEFAULTAXISTITLEWIDTH)/2.0;
  124.   xtitlebox.origin.y = bounds.origin.y + 10.0;
  125.   xtitlebox.size.width  = DEFAULTAXISTITLEWIDTH;
  126.   xtitlebox.size.height = DEFAULTFONTSIZE;
  127.   ytitlebox.origin.x = bounds.origin.x + 6.0;
  128.   ytitlebox.origin.y = YOFFSET/4.0
  129.                    + (bounds.size.height - DEFAULTAXISTITLEWIDTH)/2.0;
  130.   ytitlebox.size.width  = DEFAULTFONTSIZE;
  131.   ytitlebox.size.height = DEFAULTAXISTITLEWIDTH;
  132.   maintitlebox.origin.x = XOFFSET/4.0
  133.                        + (bounds.size.width - DEFAULTMAINTITLEWIDTH)/2.0;
  134.   maintitlebox.origin.y = bounds.size.height - 2.0 - 14.0;
  135.   maintitlebox.size.width  = DEFAULTMAINTITLEWIDTH;
  136.   maintitlebox.size.height = 14.0;
  137.  
  138.   oldbounds = bounds;
  139.  
  140.   return self;
  141. }
  142.  
  143. - (NXCoord *)xdata:(int)n    /* we use this in drawing the legend curves */
  144. {
  145.   xlegend[0] = (legendbox.origin.x + 5.33333)/ppxunit;
  146. /* We used to have 5.0 instead of 5.33333, but that can give erroneous-looking
  147.  * (albeit correct) results when writing to the screen: the pixels that are
  148.  * turned on when drawing a filled circle, for example, are much different
  149.  * when the circle's center is precisely at a pixel than when the circle's center
  150.  * is not precisely at a pixel.  The results are much better when the center
  151.  * of the circle is at a half-pixel point, and so that's what we do.
  152.  */
  153.   xlegend[1] = xlegend[0] + 40.0/ppxunit;
  154.   /* curve fragment in legend is 40 pixels in length */
  155.   if (!drawingLegendLines) xlegend[0] = 0.5*(xlegend[0] + xlegend[1]);
  156.   return xlegend;
  157. }
  158.  
  159. - (NXCoord **)ydata:(int)n    /* we use this in drawing the legend curves */
  160. {
  161.   int ncurves = [plotParam nCurvesTotal];
  162.   int j;
  163.   float yloc, yhgt=DEFAULTFONTSIZE;
  164.   const char * curvetitle;
  165.  
  166.   yhgt = [newLegendFont pointSize];
  167.  
  168.   if (yhgt==0) yhgt = DEFAULTFONTSIZE;
  169.  
  170.   yloc = (legendbox.origin.y - 2.0)/ppyunit;
  171.  
  172.   ylegend = (NXCoord **)realloc((void *)ylegend, ncurves*sizeof(NXCoord *));
  173.   for (j = 0; j < ncurves; j++) {
  174.     *(ylegend+j) = (NXCoord *)NULL; /* this is necessary */
  175.     *(ylegend+j) = (NXCoord *)realloc((void *)*(ylegend+j), 2*sizeof(NXCoord));
  176.     /* check for no lines and no symbols */
  177.     if ( ([plotParam providelinestyle:j] == NOLINE)  &&
  178.     ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
  179.     /* check for empty curvetitle */
  180.     curvetitle = [legendForm stringValueAt:j];
  181.     if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
  182.       continue;
  183.     yloc = yloc + (yhgt+0.33333)/ppyunit;
  184. /* We used to have just yhgt (instead of yhgt+0.33333); see comments in preceding
  185.  * routine for the reason to change it.
  186.  */
  187.   }
  188.  
  189.   for (j=0; j<ncurves; j++) {
  190.     *(*(ylegend+j)+0) = yloc;
  191.     *(*(ylegend+j)+1) = yloc;
  192.     /* check for no lines and no symbols */
  193.     if( ([plotParam providelinestyle:j] == NOLINE)  &&
  194.        ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
  195.     /* check for empty curvetitle */
  196.     curvetitle = [legendForm stringValueAt:j];
  197.     if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
  198.       continue;
  199.     yloc -= yhgt/ppyunit;
  200.   }
  201.   return ylegend;
  202. }
  203.  
  204. - (BOOL)has_ebars:(int)n
  205. {
  206.   return NO;
  207. }
  208.  
  209. - (int)nPoints:(int)n
  210. {
  211.   if (drawingLegendLines)  return 2;
  212.   else return 1;
  213. }
  214.  
  215. - (int)nCurves:(int)n
  216. {
  217.   return [plotParam nCurvesTotal];
  218. }
  219.  
  220. - (int)nFiles
  221. {
  222.   return 1;
  223. }
  224.  
  225. - startPlot
  226. {
  227.   const char * xtitle = [xTitle stringValueAt:0];
  228.   const char * ytitle = [yTitle stringValueAt:0];
  229.   const char * maintitle = [mainTitle stringValueAt:0];
  230.   float yhgt=DEFAULTFONTSIZE;
  231.   id mainTitleFont,axisTitleFont;
  232.  
  233.   NXSetColor([plotParam provideBackgroundColor]);
  234.   [self clear:self];
  235.   PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  236.   NXSetColor([plotParam provideTextColor]);
  237.  
  238.   /*  get new font from fontmanager */
  239.   if ([plotParam shouldChangeXTitleFont]) {
  240.     newXTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  241.   }
  242.  
  243.   yhgt = [newXTitleFont pointSize];
  244.  
  245.   if (yhgt==0) {
  246.     axisTitleFont =
  247.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  248.             style:0 matrix:NX_IDENTITYMATRIX];
  249.     yhgt = DEFAULTFONTSIZE;
  250.   }
  251.   else {
  252.     axisTitleFont = [Font newFont:[newXTitleFont name] 
  253.            size:[newXTitleFont pointSize]
  254.            style:[newXTitleFont style]
  255.            matrix:NX_IDENTITYMATRIX];
  256.   }
  257.  
  258.   [axisTitleFont set];
  259.  
  260.   xtitlebox.size.width  = [axisTitleFont getWidthOf:xtitle];
  261.   xtitlebox.size.height = yhgt;
  262.   if (oldbounds.size.width != bounds.size.width
  263.       || oldbounds.size.height != bounds.size.height) {
  264. /* Couldn't figure out how to do this with windowDidResize or superviewSizeChanged,
  265.  * so this grubby idea (with the global oldbounds variable) is used instead.
  266.  */
  267.     xtitlebox.origin.x = (xtitlebox.origin.x + 0.5*xtitlebox.size.width)
  268.       *(bounds.size.width/oldbounds.size.width) - 0.5*xtitlebox.size.width;
  269.     xtitlebox.origin.y = (xtitlebox.origin.y + 0.5*xtitlebox.size.height)
  270.       *(bounds.size.height/oldbounds.size.height) - 0.5*xtitlebox.size.height;
  271.     legendbox.origin.x = (legendbox.origin.x + 0.5*legendbox.size.width)
  272.       *(bounds.size.width/oldbounds.size.width) - 0.5*legendbox.size.width;
  273.     legendbox.origin.y = (legendbox.origin.y + 0.5*legendbox.size.height)
  274.       *(bounds.size.height/oldbounds.size.height) - 0.5*legendbox.size.height;
  275.   }
  276.   PSmoveto(xtitlebox.origin.x, xtitlebox.origin.y);
  277.   PSshow((char *)xtitle);
  278.  
  279.   /*  get new font from fontmanager */
  280.   if ([plotParam shouldChangeYTitleFont]) {
  281.     newYTitleFont =
  282.       [theFontManager convertFont:[theFontManager selFont]];
  283.   }
  284.   yhgt = [newYTitleFont pointSize];
  285.  
  286.   if (yhgt==0) {
  287.     axisTitleFont =
  288.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  289.             style:0 matrix:NX_IDENTITYMATRIX];
  290.     yhgt = DEFAULTFONTSIZE;
  291.   }
  292.   else {
  293.     axisTitleFont = [Font newFont:[newYTitleFont name] 
  294.            size:[newYTitleFont pointSize]
  295.            style:[newYTitleFont style]
  296.            matrix:NX_IDENTITYMATRIX];
  297.   }
  298.  
  299.   [axisTitleFont set];
  300.  
  301.   ytitlebox.size.width  = yhgt;
  302.   ytitlebox.size.height = [axisTitleFont getWidthOf:ytitle];
  303.   if (oldbounds.size.width != bounds.size.width
  304.       || oldbounds.size.height != bounds.size.height) {
  305.     ytitlebox.origin.x = (ytitlebox.origin.x + 0.5*ytitlebox.size.height)
  306.       *(bounds.size.width/oldbounds.size.width) - 0.5*ytitlebox.size.height;
  307.     ytitlebox.origin.y = (ytitlebox.origin.y + 0.5*ytitlebox.size.width)
  308.       *(bounds.size.height/oldbounds.size.height) - 0.5*ytitlebox.size.width;
  309.   }
  310.   PSmoveto(ytitlebox.origin.x + yhgt, ytitlebox.origin.y);
  311.   PSgsave();
  312.   PSrotate(90.0);
  313.   PSshow((char *)ytitle);
  314.   PSgrestore();
  315.   
  316.   if ([plotParam shouldChangeMainTitleFont]) {
  317.     newMainTitleFont =
  318.       [theFontManager convertFont:[theFontManager selFont]];
  319.   }
  320.   yhgt = [newMainTitleFont pointSize];
  321.  
  322.   if (yhgt==0) {
  323.     mainTitleFont =
  324.       [Font newFont:"Helvetica" size:14.0 style:0 matrix:NX_IDENTITYMATRIX];
  325.     yhgt = 14.0;
  326.   }
  327.   else {
  328.     mainTitleFont = [Font newFont:[newMainTitleFont name] 
  329.            size:[newMainTitleFont pointSize]
  330.            style:[newMainTitleFont style]
  331.            matrix:NX_IDENTITYMATRIX];
  332.   }
  333.  
  334.   [mainTitleFont set];
  335.  
  336.   maintitlebox.size.width  = [mainTitleFont getWidthOf:maintitle];
  337.   maintitlebox.size.height = yhgt;
  338.   if (oldbounds.size.width != bounds.size.width
  339.       || oldbounds.size.height != bounds.size.height) {
  340.     maintitlebox.origin.x = (maintitlebox.origin.x + 0.5*maintitlebox.size.width)
  341.       *(bounds.size.width/oldbounds.size.width) - 0.5*maintitlebox.size.width;
  342.     maintitlebox.origin.y = (maintitlebox.origin.y + 0.5*maintitlebox.size.height)
  343.       *(bounds.size.height/oldbounds.size.height) - 0.5*maintitlebox.size.height;
  344.     oldbounds = bounds;
  345.   }
  346.   PSmoveto(maintitlebox.origin.x, maintitlebox.origin.y);
  347.   PSshow((char *)maintitle);
  348.  
  349.   // box around the plot:
  350.   if ([borderBoxOnOff state]) {
  351.     PSsetlinewidth([borderBoxThicknessText floatValue]);
  352.     PSmoveto(0.0, 0.0);
  353.     PSlineto(bounds.size.width, 0.0);
  354.     PSlineto(bounds.size.width, bounds.size.height);
  355.     PSlineto(0.0, bounds.size.height);
  356.     PSlineto(0.0, 0.0);
  357.     PSstroke();
  358.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  359.   }
  360.   // The preceding doesn't work quite right: for some reason the vertical line
  361.   // on the right hand side of the plot doesn't show up on the display.  The
  362.   // line is there if you print or preview the file, however.
  363.   return self;
  364. }
  365.  
  366. - setDrawColor:(float) color
  367. {
  368.   PSsetgray(color);
  369.   return self;
  370. }
  371.  
  372. - drawLines:sender :(BOOL)xaxislog :(BOOL)yaxislog
  373. /*
  374.  * This is coded so that when drawLines is called with plotParam as argument,
  375.  * it draws the data curves; when drawLines is called with self (plotView) as
  376.  * argument, it draws the short line segments in the legend box.
  377.  */
  378. {
  379.   int i, j, jrun, n;
  380.   NXCoord *x;
  381.   NXCoord **y;
  382.   int npoints, ncurves;
  383.   int curveindex = 0;        /* cumulative index */
  384.   int linestyle;
  385.   float thick = [lineThicknessText floatValue];
  386.   float pattern0[] = {};    /* solid      */
  387.   float pattern1[] = {4.0, 4.0}; /* dash       */
  388.   float pattern2[] = {1.0, 3.0}; /* dot        */
  389.   float pattern3[] = {7.0, 3.0, 3.0, 3.0}; /* chain dash */
  390.   float pattern4[] = {7.0, 4.0, 1.0, 4.0}; /* chain dot  */
  391.   const char * curvetitle;
  392.  
  393.   for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
  394.     x  = [sender xdata:n];
  395.     y  = [sender ydata:n];
  396.     ncurves = [sender nCurves:n];
  397.     npoints = [sender nPoints:n];
  398.     PSnewpath();
  399.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? thick : MAX(thick, 0.4));
  400.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  401.       linestyle = [plotParam providelinestyle:jrun];
  402.       NXSetColor([plotParam provideCurveColor:jrun]);
  403.       switch(linestyle) {
  404.       case SOLID:
  405.     PSsetdash(pattern0, 0, 0.0);
  406.     break;
  407.       case DASH:
  408.     PSsetdash(pattern1, 2, 0.0);
  409.     break;
  410.       case DOT:
  411.     PSsetdash(pattern2, 2, 0.0);
  412.     break;
  413.       case CHAINDASH:
  414.     PSsetdash(pattern3, 4, 0.0);
  415.     break;
  416.       case CHAINDOT:
  417.     PSsetdash(pattern4, 4, 0.0);
  418.     break;
  419.       case NOLINE:            /* no lines */
  420.     continue;
  421.       }
  422.       if (linestyle == NOLINE) continue; /* no lines, go to next curve */
  423.       /* If we're drawing the legend and there is no title, don't
  424.        * bother to draw the line:
  425.        */
  426.       curvetitle = [legendForm stringValueAt:jrun];
  427.       if (drawingLegendLines && curvetitle[0]=='\0') continue;
  428.       j = jrun - curveindex;    /* for indexing into the y array */
  429.       if (!xaxislog && !yaxislog) {
  430.     PSmoveto(x[0]*ppxunit, *(*(y + j)) * ppyunit );
  431.     for (i=1; i<npoints; i++) {
  432.       if ( (i % 512) == 0 ) {
  433.         PSstroke();
  434.         PSnewpath();
  435.         PSmoveto(x[i-1]*ppxunit, *(*(y+j)+i-1) * ppyunit);
  436.       }
  437.       PSlineto(x[i]*ppxunit, *(*(y+j)+i) * ppyunit);
  438.     }
  439.     PSstroke();
  440.       }
  441.       else if (!xaxislog && yaxislog) {
  442.     PSmoveto(x[0]*ppxunit, (float)log10((double)*(*(y + j))) * ppyunit );
  443.     for (i=1; i<npoints; i++) {
  444.       if ( (i % 512) == 0 ) {
  445.         PSstroke();
  446.         PSnewpath();
  447.         PSmoveto(x[i-1]*ppxunit, 
  448.              (float)log10((double)*(*(y+j)+i-1)) * ppyunit);
  449.       }
  450.       PSlineto(x[i]*ppxunit, (float)log10((double)*(*(y+j)+i)) * ppyunit);
  451.     }
  452.     PSstroke();
  453.       }
  454.       else if (xaxislog && !yaxislog) {
  455.     PSmoveto((float)log10((double)x[0])*ppxunit, *(*(y + j)) * ppyunit );
  456.     for (i=1; i<npoints; i++) {
  457.       if ( (i % 512) == 0 ) {
  458.         PSstroke();
  459.         PSnewpath();
  460.         PSmoveto((float)log10((double)x[i-1])*ppxunit,
  461.              *(*(y+j)+i-1) * ppyunit);
  462.       }
  463.       PSlineto((float)log10((double)x[i])*ppxunit, *(*(y+j)+i) * ppyunit);
  464.     }
  465.     PSstroke();
  466.       }
  467.       else if (xaxislog && yaxislog) {
  468.     PSmoveto((float)log10((double)x[0])*ppxunit,
  469.          (float)log10((double)*(*(y + j))) * ppyunit );
  470.     for (i=1; i<npoints; i++) {
  471.       if ( (i % 512) == 0 ) {
  472.         PSstroke();
  473.         PSnewpath();
  474.         PSmoveto((float)log10((double)x[i-1])*ppxunit,
  475.              (float)log10((double)*(*(y+j)+i-1)) * ppyunit);
  476.       }
  477.       PSlineto((float)log10((double)x[i])*ppxunit,
  478.            (float)log10((double)*(*(y+j)+i)) * ppyunit);
  479.     }
  480.     PSstroke();
  481.       }
  482.     }
  483.     curveindex += ncurves;
  484.   }
  485.   PSsetdash(pattern0, 0, 0.0);    /* back to solid lines  */
  486.   return self;
  487. }
  488.  
  489. /*
  490.  * Our first idea for drawing the symbols was: draw each symbol just once
  491.  * in an off-screen window, then composite them in where needed.  This worked
  492.  * fine for screen drawing (modulo a little difficulty in compositing to just
  493.  * the right spot), but was a miserable failure when it came to printing: the
  494.  * print machinery scaled the composite bitmap.  We finally decided to abandon
  495.  * compositing, in the interest of keeping our screen drawing code the same as
  496.  * our printing code.  Should check this again under version 2.0.
  497.  */
  498. - drawSymbols:sender :(BOOL)xaxislog :(BOOL)yaxislog
  499. /*
  500.  * This is coded so that when drawSymbols is called with plotParam as argument,
  501.  * it draws the data curves; when drawSymbols is called with self (plotView) as
  502.  * argument, it draws the symbols in the legend box.
  503.  */
  504. {
  505. /* Our current code in drawSymbols could perhaps be sped up by using wraps
  506.  * or DPSuserpath(). See if it's too slow as it stands before trying to
  507.  * speed it up.
  508.  */
  509.   int i, j, jrun, n;
  510.   float size = 4.0;
  511.   NXCoord *x;
  512.   NXCoord **y;
  513.   int npoints, ncurves;
  514.   int curveindex = 0;
  515.   int symbolstyle;
  516.   register float xtmp, ytmp;
  517.   const char * curvetitle;
  518.  
  519.   size = [symbolSizeText floatValue];
  520.  
  521.   PSgsave();
  522.   for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
  523.     x = [sender xdata:n];
  524.     y = [sender ydata:n];
  525.     ncurves = [sender nCurves:n];
  526.     npoints = [sender nPoints:n];
  527.     PSnewpath();
  528.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  529.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  530.       symbolstyle = [plotParam providesymbolstyle:jrun];
  531.       NXSetColor([plotParam provideCurveColor:jrun]);
  532.       /* If we're drawing the legend and there is no title, don't
  533.        * bother to draw the symbol:
  534.        */
  535.       curvetitle = [legendForm stringValueAt:jrun];
  536.       if (drawingLegendSymbols && curvetitle[0]=='\0') continue;
  537.       j = jrun - curveindex;    /* for indexing into the y array */
  538.       switch(symbolstyle) {
  539.       case NOSYMBOL:
  540.     continue;
  541.     break;
  542.       case CIRCLE:
  543.     for (i=0; i<npoints; i++) {
  544.       /*      circle_at(x[i], *(*(y+j)+i));      */
  545.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  546.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  547.       PSarc(xtmp * ppxunit, ytmp * ppyunit, size, 0.0, 360.0);
  548.       PSfill();
  549.     }
  550.     break;
  551.       case XMARK:
  552.     for (i=0; i<npoints; i++) {
  553.       /*      x_at(x[i], *(*(y+j)+i));           */
  554.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  555.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  556.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  - size);
  557.       PSrlineto(2.0*size, 2.0*size);
  558.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  + size);
  559.       PSrlineto(2.0*size, -2.0*size);
  560.       PSstroke();
  561.     }
  562.     break;
  563.       case UPTRIANGLE:
  564.     for (i=0; i<npoints; i++) {
  565.       /*      uptriangle_at(x[i], *(*(y+j)+i));    */
  566.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  567.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  568.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  - size/SQRT3);
  569.       PSrlineto(2.0*size, 0.0);
  570.       PSrlineto(-size, size*SQRT3);
  571.       PSclosepath();
  572.       PSfill();
  573.     }
  574.     break;
  575.       case DOWNTRIANGLE:
  576.     for (i=0; i<npoints; i++) {
  577.       /*      downtriangle_at(x[i], *(*(y+j)+i));     */
  578.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  579.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  580.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  + size/SQRT3);
  581.       PSrlineto(2.0*size, 0.0);
  582.       PSrlineto(-size, -size*SQRT3);
  583.       PSclosepath();
  584.       PSfill();
  585.     }
  586.     break;
  587.       case DIAMOND:
  588.     for (i=0; i<npoints; i++) {
  589.       /*      diamond_at(x[i], *(*(y+j)+i));          */
  590.       /* 3 pixels horizontal, 4 pixels vertical */
  591.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  592.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  593.       PSmoveto(xtmp * ppxunit, ytmp * ppyunit  - size);
  594.       PSrlineto(3.0/4.0*size, size);    
  595.       PSrlineto(-3.0/4.0*size, size); /* gives a pleasing diamond shape */
  596.       PSrlineto(-3.0/4.0*size, -size);
  597.       PSclosepath();
  598.       PSfill();
  599.     }
  600.     break;
  601.       case SQUARE:
  602.     for (i=0; i<npoints; i++) {
  603.       /*      square_at(x[i], *(*(y+j)+i));            */
  604.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  605.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  606.       PSrectfill(xtmp * ppxunit  - size, ytmp * ppyunit  - size,
  607.              2.0*size, 2.0*size);
  608.     }
  609.     break;
  610.       case PLUS:
  611.     for (i=0; i<npoints; i++) {
  612.       /*      plus_at(x[i], *(*(y+j)+i));              */
  613.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  614.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  615.       PSmoveto(xtmp * ppxunit, ytmp * ppyunit  - size);
  616.       PSrlineto(0.0, 2.0*size);
  617.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit);
  618.       PSrlineto(2.0*size, 0.0);
  619.       PSstroke();
  620.     }
  621.     break;
  622.       }
  623.     }
  624.     curveindex += ncurves;
  625.   }
  626.   PSgrestore();
  627.  
  628.   return self;
  629. }
  630.  
  631. - drawErrorBars :(BOOL)xaxislog :(BOOL)yaxislog
  632. {
  633.   int     n, j, npoints, ncurves, jrun, i;
  634.   NXCoord *x, *ex = NULL;    /* ex and ey initialized to avoid */
  635.   NXCoord **y, **ey = NULL;    /* compiler warning               */
  636.   int     curveindex = 0;
  637.   register float xtmp, ytmp, tmp1, tmp2;
  638.   float   width = [errorBarBaseWidth floatValue];
  639.   BOOL    exbars, eybars;
  640.  
  641.   PSgsave();
  642.   for (n=0; n<[plotParam nFiles]; n++) { /* loop over all active files */
  643.     exbars = [plotParam has_exbars:n];
  644.     eybars = [plotParam has_eybars:n];
  645.     ncurves = [plotParam nCurves:n];
  646.     if (!exbars  && !eybars ) {       /* skip if no error bars */
  647.       curveindex += ncurves;          /* but have to bump this index, */
  648.       continue;                  /* to get the colors correct    */
  649.     }
  650.     x = [plotParam xdata:n];    /* we have to draw error bars */
  651.     if (exbars) {
  652.       ex = [plotParam exdata:n];
  653.     }
  654.     y = [plotParam ydata:n];
  655.     if (eybars) {
  656.       ey = [plotParam eydata:n];
  657.     }
  658.     npoints = [plotParam nPoints:n];
  659.     PSnewpath();
  660.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  661.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  662.       NXSetColor([plotParam provideCurveColor:jrun]);
  663.       j = jrun - curveindex;    /* for indexing into the y and ey arrays */
  664.       /* check if error bars are desired */
  665.       if (exbars &&  [[errorBarMatrix cellAt :n :0] state] == 1) {
  666.     for (i=0; i<npoints; i++) {
  667.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  668.       if (xaxislog) {
  669.         if (x[i] - ex[i] <= 0.0) { /* avoid log of negative */
  670.           tmp1 = (float)log10( (double) x[i] ); /* what to do? */
  671.         }
  672.         else {
  673.           tmp1 = (float)log10( (double) (x[i] - ex[i]) );
  674.         }
  675.         if (x[i] + ex[i] <= 0.0) { /* again avoid log of negative */
  676.           tmp2 = (float)log10( (double) x[i] );
  677.         }
  678.         else {
  679.           tmp2 = (float)log10( (double) (x[i] + ex[i]) );
  680.         }
  681.       }
  682.       else {
  683.         tmp1 = x[i] - ex[i];
  684.         tmp2 = x[i] + ex[i];
  685.       }
  686.       PSmoveto(tmp1 * ppxunit, ytmp * ppyunit);
  687.       PSrlineto((tmp2-tmp1)*ppxunit, 0.0);
  688.       if (width > 0.0) {
  689.         PSmoveto(tmp1 * ppxunit, ytmp*ppyunit - width/2.0);
  690.         PSrlineto(0.0, width);
  691.         PSmoveto(tmp2 * ppxunit, ytmp*ppyunit - width/2.0);
  692.         PSrlineto(0.0, width);
  693.       }
  694.       PSstroke();
  695.     }
  696.       }
  697.       if (eybars && [[errorBarMatrix cellAt :n :j+1] state] == 1) {
  698.     for (i=0; i<npoints; i++) {
  699.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  700.       if (yaxislog) {
  701.         if ((*(*(y+j)+i) - *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
  702.           tmp1 = (float)log10( (double) (*(*(y+j)+i)) );
  703.         }
  704.         else {
  705.           tmp1 = (float)log10( (double) (*(*(y+j)+i) - *(*(ey+j)+i)) );
  706.         }
  707.         if ((*(*(y+j)+i) + *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
  708.           tmp2 = (float)log10( (double) (*(*(y+j)+i)) );
  709.         }
  710.         else {
  711.           tmp2 = (float)log10( (double) (*(*(y+j)+i) + *(*(ey+j)+i)) );
  712.         }
  713.       }
  714.       else {
  715.         tmp1 = *(*(y+j)+i) - *(*(ey+j)+i);
  716.         tmp2 = *(*(y+j)+i) + *(*(ey+j)+i);
  717.       }
  718.       PSmoveto(xtmp * ppxunit, tmp1 * ppyunit);
  719.       PSrlineto(0.0, (tmp2-tmp1)*ppyunit);
  720.       if (width > 0.0) {
  721.         PSmoveto(xtmp*ppxunit - width/2.0, tmp1 * ppyunit);
  722.         PSrlineto(width, 0.0);
  723.         PSmoveto(xtmp*ppxunit - width/2.0, tmp2 * ppyunit);
  724.         PSrlineto(width, 0.0);
  725.       }
  726.       PSstroke();
  727.     }
  728.       }
  729.     }
  730.     curveindex += ncurves;
  731.   }
  732.   PSgrestore();
  733.  
  734.   return self;
  735. }
  736.  
  737.  
  738. - startLegend
  739. {
  740.   id legendtextfont,legendTitleFont;
  741.   const char * curvetitle;
  742.   const char * legendtitle = [legendTitle stringValueAt:0];
  743.   int j;
  744.   int ncurves = [plotParam nCurvesTotal];
  745.   float maxcurvetitlewid = 0;
  746.   float yhgt = DEFAULTFONTSIZE;
  747.  
  748.   if ([plotParam shouldChangeLegendFont]) {
  749.     newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
  750.   }
  751.  
  752.   yhgt = [newLegendFont pointSize];    /* height of text */
  753.  
  754.   /* get legend text font initialized */
  755.   if (yhgt==0) {
  756.     legendtextfont =
  757.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  758.             style:0 matrix:NX_IDENTITYMATRIX];
  759.     yhgt = DEFAULTFONTSIZE;
  760.   }
  761.   else {
  762.     legendtextfont = [Font newFont:[newLegendFont name] 
  763.                    size:[newLegendFont pointSize]
  764.                    style:[newLegendFont style]
  765.                    matrix:NX_IDENTITYMATRIX];
  766.   }
  767.  
  768.   [legendtextfont set];
  769.  
  770.   legendbox.size.height = 10.0;
  771.  
  772.   for (j=0; j<ncurves; j++) {
  773.     /* check for no lines and no symbols */
  774.     if( ([plotParam providelinestyle:j] == NOLINE)  &&
  775.        ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
  776.     curvetitle = [legendForm stringValueAt:j];
  777.     /* skip this curve if there is no title: */
  778.     if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
  779.     maxcurvetitlewid = MAX(maxcurvetitlewid,
  780.                [legendtextfont getWidthOf:curvetitle]);
  781.     legendbox.size.height += yhgt; /* increment by yhgt for every curve */
  782.   }
  783.  
  784.   /* check on legend title width also */
  785.  
  786.   if ([plotParam shouldChangeLegendTitleFont]) {
  787.     newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  788.   }
  789.  
  790.   yhgt = [newLegendTitleFont pointSize];    /* height of text */
  791.  
  792.   if (yhgt==0) {
  793.     legendTitleFont =
  794.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  795.             style:0 matrix:NX_IDENTITYMATRIX];
  796.     yhgt = DEFAULTFONTSIZE;
  797.   }
  798.   else {
  799.     legendTitleFont = [Font newFont:[newLegendTitleFont name] 
  800.                     size:[newLegendTitleFont pointSize]
  801.                     style:[newLegendTitleFont style]
  802.                     matrix:NX_IDENTITYMATRIX];
  803.   }
  804.  
  805.   [legendTitleFont set];
  806.   maxcurvetitlewid = MAX(maxcurvetitlewid,
  807.                [legendTitleFont getWidthOf:legendtitle]);
  808.  
  809.   legendbox.size.width = 5.0 + 40.0 + 5.0 + maxcurvetitlewid + 5.0;
  810.   /* legendboxwidth = L. margin + 40 + space + curve title + R. margin */
  811.   if([legendTitleFont getWidthOf:legendtitle] != 0)
  812.     legendbox.size.height = legendbox.size.height + 2.0*yhgt - 4.0;
  813.   return self;
  814. }
  815.  
  816. - drawLegend:sender
  817. {
  818.   id legendtextfont,legendTitleFont;
  819.   const char * curvetitle;
  820.   const char * legendtitle = [legendTitle stringValueAt:0];
  821.   int j;
  822.   int ncurves = [plotParam nCurvesTotal];
  823.   float yhgt = DEFAULTFONTSIZE;
  824.  
  825.   if ([plotParam shouldChangeLegendFont]) {
  826.     newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
  827.   }
  828.  
  829.   yhgt = [newLegendFont pointSize];
  830.  
  831.   /* get legend text font initialized */
  832.   if (yhgt==0) {
  833.     legendtextfont =
  834.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  835.             style:0 matrix:NX_IDENTITYMATRIX];
  836.     yhgt = DEFAULTFONTSIZE;
  837.   }
  838.   else {
  839.     legendtextfont = [Font newFont:[newLegendFont name] 
  840.                    size:[newLegendFont pointSize]
  841.                    style:[newLegendFont style]
  842.                    matrix:NX_IDENTITYMATRIX];
  843.   }
  844.  
  845.   [legendtextfont set];
  846.  
  847.   drawingLegendLines = YES;
  848.   [self xdata:0];
  849.   [self ydata:0];
  850.  
  851.   NXSetColor([plotParam provideBackgroundColor]);
  852.  
  853.   if ([legendOpaque state] == 0) {
  854.     NXRectFill(&legendbox);
  855.   }
  856.  
  857.   [sender drawLines:sender :NO :NO];    /* legend lines, linear axes always */
  858.  
  859.   NXSetColor([plotParam provideTextColor]);
  860.  
  861.   for (j=0; j<ncurves; j++) {
  862.     /* check for no lines and no symbols */
  863.     if ( ([plotParam providelinestyle:j] == NOLINE)  &&
  864.     ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
  865.     curvetitle = [legendForm stringValueAt:j];
  866.     /* skip this curve if there is no title: */
  867.     if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
  868.     PSmoveto(xlegend[1]*ppxunit + 5.0, 
  869.          *(*(ylegend+j)+0) * ppyunit - 0.33*yhgt);
  870.     PSshow((char *)curvetitle);
  871.   }
  872.  
  873.   if ([plotParam shouldChangeLegendTitleFont]) {
  874.     newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  875.   }
  876.  
  877.   yhgt = [newLegendTitleFont pointSize];    /* height of text */
  878.   if (yhgt==0) {
  879.     legendTitleFont =
  880.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  881.             style:0 matrix:NX_IDENTITYMATRIX];
  882.     yhgt = DEFAULTFONTSIZE;
  883.   }
  884.   else {
  885.     legendTitleFont = [Font newFont:[newLegendTitleFont name] 
  886.                     size:[newLegendTitleFont pointSize]
  887.                     style:[newLegendTitleFont style]
  888.                     matrix:NX_IDENTITYMATRIX];
  889.   }
  890.  
  891.   [legendTitleFont set];
  892.   PSmoveto(xlegend[0]*ppxunit - 5.0 + legendbox.size.width/2.0
  893.        - 0.5*[legendTitleFont getWidthOf:legendtitle],
  894.        *(*(ylegend+0)+0) * ppyunit + yhgt);
  895.   PSshow((char *)legendtitle);
  896.   if([legendBoxOnOff state]) {
  897.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  898.     PSrectstroke(legendbox.origin.x, legendbox.origin.y,
  899.          legendbox.size.width, legendbox.size.height);
  900.   }
  901.   drawingLegendLines = NO;
  902.   drawingLegendSymbols = YES;
  903.   [sender drawSymbols:sender :NO :NO];    /* legend symbols, linear axes always */
  904.   drawingLegendSymbols = NO;
  905.  
  906.   return self;
  907. }
  908.  
  909. - clear:sender
  910. {
  911.   /*
  912.    * Derek Lisoski (dlisoski@cco.caltech.edu) suggests not drawing the
  913.    * opaque background rectangle when you print or save a file.  Then
  914.    * when you read the saved plots into a separate drawing program you
  915.    * can overlay multiple plots or get the plots arbitrarily close to
  916.    * each other.  There may be some problems with background colors
  917.    * when importing into various applications (Create or Draw, e.g.)
  918.    */
  919.   if (NXDrawingStatus == NX_DRAWING || [opaqueBackgroundButton state] == 0) {
  920.     NXRectFill(&bounds);
  921.   /* for color; had NXEraseRect, but that always fills with white    */
  922.   }
  923.   return self;
  924. }
  925.  
  926. /*
  927.  * This routine assumes it is called with xmin != xmax and ymin != ymax.
  928.  * Bad things may happen if this is not the case.
  929.  * The input parameters are assumed to be in pixel coordinates.
  930.  */
  931. - drawTicMarks:(float)xmin :(float)xmax :(float)ymin :(float)ymax
  932. {
  933.   float pattern0[] = {};    /* solid      */
  934.   float pattern2[] = {1.0, 3.0}; /* dot        */
  935.   double xinc_unscaled = [plotParam provideXinc];
  936.   double yinc_unscaled = [plotParam provideYinc];
  937.   double xmin_unscaled = [plotParam provideXmin];
  938.   double ymin_unscaled = [plotParam provideYmin];
  939.   double xmax_unscaled = [plotParam provideXmax];
  940.   double ymax_unscaled = [plotParam provideYmax];
  941.   /* It is useful in some fairly extreme cases to have the increments
  942.    * not in pixel coordinates (otherwise can get "bad" labels).
  943.    */
  944.   char ticlabel[32];
  945.   id ticfont, ticfont1;
  946.   float x, y, ticloc_rat, xticloc, yticloc;
  947.   BOOL drawGrid = [gridOnOff state];
  948.   BOOL xaxislog = [plotParam xaxisLog];
  949.   BOOL yaxislog = [plotParam yaxisLog];
  950.   BOOL drawMinorTics = [minorTicMarksOnOff state];
  951.   int  ticLocation;        /* 0=axes, 1=2 sides, 2=4 sides */
  952.   float  ticmarklen = [ticMarkLengthText floatValue];
  953.   int    nmin, ninc, nmax, j, i;
  954.   float  ticloc, xwid, yhgt=DEFAULTFONTSIZE, yhgt1;
  955.   double first;
  956.   int    nlabels;
  957.   int    axformat[3];
  958.  
  959.  
  960.   if (strncmp([ticMarkLocation title], "Axes", 4) == 0)
  961.     ticLocation = 0;
  962.   else if (strncmp([ticMarkLocation title], "Frame (2 sides)", 15) == 0)
  963.     ticLocation = 1;
  964.   else
  965.     ticLocation = 2;
  966.  
  967.   if ([plotParam shouldChangeTicLabelFont]) {
  968.     newTicLabelFont = [theFontManager convertFont:[theFontManager selFont]];
  969.   }
  970.  
  971.   yhgt = [newTicLabelFont pointSize];
  972.   if (yhgt==0) {
  973.     ticfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  974.                 style:0 matrix:NX_IDENTITYMATRIX];
  975.     ticfont1 =
  976.       [Font newFont:"Helvetica" size:10.0 style:0 matrix:NX_IDENTITYMATRIX];
  977.     yhgt = DEFAULTFONTSIZE;
  978.   }
  979.   else {
  980.     ticfont = [Font newFont:[newTicLabelFont name] 
  981.                 size:[newTicLabelFont pointSize]
  982.                 style:[newTicLabelFont style]
  983.                 matrix:NX_IDENTITYMATRIX];
  984.  
  985.     yhgt1 = (yhgt >= 10.0? yhgt - 2.0 : 8.0);
  986.     ticfont1 = [Font newFont:[newTicLabelFont name] 
  987.                  size:yhgt1
  988.                  style:[newTicLabelFont style]
  989.                  matrix:NX_IDENTITYMATRIX];
  990.   }
  991.  
  992.   /* get tic font initialized */
  993.   [ticfont set];
  994.  
  995.   PSnewpath();
  996.   //  PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4);
  997.   PSsetlinewidth([ticMarkThicknessText floatValue]);
  998.   if (xaxislog) {        /* x-axis is logarithmic */
  999.     nmin = (int)floor((double)(xmin/ppxunit));
  1000.     nmax = (int)ceil((double)(xmax/ppxunit));
  1001.     ninc = (int)rint(log10(xinc_unscaled));
  1002.     if (ninc == 0) ninc = 1;    /* avoid infinite loop */
  1003.     if ([handFormatXaxis state] == 1) {
  1004.       axformat[0] = [xFormatLeft intValue];
  1005.       axformat[1] = [xFormatRight intValue];
  1006.       axformat[2] = [xFormatExponent intValue];
  1007.     }
  1008.     for (j=nmin; j<=nmax; j+=ninc) {
  1009.       if ( (float)j * ppxunit >= xmin &&  (float)j * ppxunit <= xmax ) {
  1010.     if (drawGrid) {
  1011.       PSsetlinewidth([gridThicknessText floatValue]);
  1012.       PSmoveto((float)j * ppxunit, ymin);
  1013.       if ([gridDotted state])
  1014.         PSsetdash(pattern2, 2, 0.0);
  1015.       else
  1016.         PSsetdash(pattern0, 0, 0.0);
  1017.       PSrlineto(0.0, ABS(ymax-ymin));
  1018.       PSstroke();
  1019.       PSsetlinewidth([ticMarkThicknessText floatValue]);
  1020.     }
  1021.     PSmoveto((float)j * ppxunit, ymin - ticmarklen*6.0); /* big tic mark */
  1022.     PSsetdash(pattern0, 0, 0.0);
  1023.     PSrlineto(0.0, ticmarklen*6.0);
  1024.     PSstroke();
  1025.     if (ticLocation == 2) {    /* tics on right and top */
  1026.       PSmoveto((float)j * ppxunit, ymax); /* big tic mark */
  1027.       PSsetdash(pattern0, 0, 0.0);
  1028.       PSrlineto(0.0, ticmarklen*6.0);
  1029.       PSstroke();
  1030.     }
  1031.     if ([handFormatXaxis state] == 0) {
  1032.       ticloc_rat = yhgt/DEFAULTFONTSIZE;
  1033.       PSmoveto((float)j * ppxunit - ticloc_rat*8.0,
  1034.            ymin - MAX(24.0*ticloc_rat,
  1035.                   24.0*ticloc_rat*(ticmarklen+10.0)/10.0));
  1036.       PSshow("10");
  1037.       [ticfont1 set];
  1038.       PSmoveto((float)j * ppxunit + ticloc_rat*4.0,
  1039.            ymin - MAX(16.0*ticloc_rat,
  1040.                    8.0*ticloc_rat*(2.0 + 0.3*ticmarklen)));
  1041.       sprintf(ticlabel, "%-5d", j);
  1042.       PSshow(ticlabel);
  1043.       [ticfont set];
  1044.     }
  1045.     else {
  1046.       x = (float) pow((double)10.0, (double)j);
  1047.       handformat(x, ticlabel, axformat);
  1048.       xwid = [ticfont getWidthOf:ticlabel];
  1049.       PSmoveto((float)j * ppxunit - xwid/2.0,
  1050.             ymin - yhgt - MAX(5.0, 5.0*ticmarklen));
  1051.       PSshow(ticlabel);
  1052.     }
  1053.       }
  1054.       if (drawMinorTics) {
  1055.     for (i=2; i<=9; i++) {
  1056.       ticloc = (float)j * ppxunit + ppxunit*(float)log10((double)i);
  1057.       if ( ticloc>xmin && ticloc<xmax ) {
  1058.         PSmoveto(ticloc, ymin - ticmarklen*3.0); /* small tic mark */
  1059.         PSrlineto(0.0, ticmarklen*3.0);
  1060.         PSstroke();
  1061.         if (ticLocation == 2) { /* tics on right and top */
  1062.           PSmoveto(ticloc, ymax); /* small tic mark */
  1063.           PSrlineto(0.0, ticmarklen*3.0);
  1064.           PSstroke();
  1065.         }
  1066.       }
  1067.     }
  1068.       }
  1069.     }
  1070.   }
  1071.   else {            /* x-axis is linear */
  1072.     yticloc = (ticLocation > 0 ? ymin : 2.0*ticmarklen) ;
  1073.     /* If inc is big, skip tic marks entirely */
  1074.     if (fabs(xinc_unscaled) < fabs(xmax_unscaled - xmin_unscaled)) {
  1075.       count_labels(&nlabels, &first, xmin_unscaled, xinc_unscaled, xmax_unscaled);
  1076.       if ([handFormatXaxis state] == 1) {
  1077.     axformat[0] = [xFormatLeft intValue];
  1078.     axformat[1] = [xFormatRight intValue];
  1079.     axformat[2] = [xFormatExponent intValue];
  1080.       }
  1081.       else {
  1082.     autoformat(xmin_unscaled, xinc_unscaled, xmax_unscaled, axformat);
  1083.     [xFormatLeft setIntValue:axformat[0]];
  1084.     [xFormatRight setIntValue:axformat[1]];
  1085.     [xFormatExponent setIntValue:axformat[2]];
  1086.       }
  1087.       /*
  1088.        * next loop starts at -1 because there may be room for minor tic
  1089.        * marks to the left of the first major tic mark (after a zoom, e.g.)
  1090.        */
  1091.       for (i = -1; i < nlabels; i++) {
  1092.     /* Special test here for what should be exact 0 (but isn't sometimes
  1093.      * due to floating-point arithmetic.
  1094.      */
  1095.     if (fabs(first/xinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
  1096.       x = 0.0;
  1097.     }
  1098.     else {
  1099.       x = (first + (xinc_unscaled)*(float)i) * ppxunit;
  1100.     }
  1101.     if (x >= xmin) {    /* ensure major tic mark won't be off edge */
  1102.       if (drawGrid) {
  1103.         PSsetlinewidth([gridThicknessText floatValue]);
  1104.         PSmoveto(x, ymin);
  1105.         if ([gridDotted state])
  1106.           PSsetdash(pattern2, 2, 0.0);
  1107.         else
  1108.           PSsetdash(pattern0, 0, 0.0);
  1109.         PSrlineto(0.0, ABS(ymax-ymin));
  1110.         PSstroke();
  1111.         PSsetlinewidth([ticMarkThicknessText floatValue]);
  1112.       }
  1113.       /* Nothing at 0 if we're putting tic marks on axes:  */
  1114.       if (ticLocation > 0 || x != 0.0) {
  1115.         PSmoveto(x, yticloc - ticmarklen*4.0);
  1116.         PSsetdash(pattern0, 0, 0.0);
  1117.         PSrlineto(0.0, ticmarklen*4.0);
  1118.         PSstroke();
  1119.             if (ticLocation == 2) { /* tics on right and top */
  1120.           PSmoveto(x, ymax);
  1121.           PSsetdash(pattern0, 0, 0.0);
  1122.           PSrlineto(0.0, ticmarklen*4.0);
  1123.           PSstroke();
  1124.             }
  1125.         handformat(x/ppxunit, ticlabel, axformat);
  1126.         xwid = [ticfont getWidthOf:ticlabel];
  1127.         PSmoveto(x - xwid/2.0, yticloc - yhgt - MAX(5.0, 5.0*ticmarklen));
  1128.         PSshow(ticlabel);
  1129.       }
  1130.     }
  1131.     if (drawMinorTics) {
  1132.           if (ticLocation > 0) { /* tic marks on frame */
  1133.         for (j=1; j<=9; j++) {
  1134.           ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
  1135.           if (ticloc>xmin && ticloc<xmax) {
  1136.             PSmoveto(ticloc, yticloc - ticmarklen*2.0);
  1137.             PSrlineto(0.0, ticmarklen*2.0);
  1138.             PSstroke();
  1139.                 if (ticLocation == 2) {    /* tics on right and top */
  1140.                 PSmoveto(ticloc, ymax);
  1141.               PSrlineto(0.0, ticmarklen*2.0);
  1142.               PSstroke();
  1143.                 }
  1144.               }
  1145.             }
  1146.           }
  1147.           else {
  1148.         for (j=1; j<=9; j++) {
  1149.           ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
  1150.           if (ticloc>xmin && ticloc<xmax) {
  1151.             PSmoveto(ticloc, -ticmarklen);
  1152.             PSrlineto(0.0, 2.0*ticmarklen);
  1153.             PSstroke();
  1154.               }
  1155.             }
  1156.           }
  1157.     }
  1158.       }
  1159.     }
  1160.   }
  1161.   if (yaxislog) {        /* y-axis is logarithmic */
  1162.     nmin = (int)floor((double)(ymin/ppyunit));
  1163.     nmax = (int)ceil((double)(ymax/ppyunit));
  1164.     ninc = (int)rint(log10(yinc_unscaled));
  1165.     if (ninc == 0) ninc = 1;    /* avoid infinite loop */
  1166.     if ([handFormatYaxis state] == 1) {
  1167.       axformat[0] = [yFormatLeft intValue];
  1168.       axformat[1] = [yFormatRight intValue];
  1169.       axformat[2] = [yFormatExponent intValue];
  1170.     }
  1171.     for (j=nmin; j<=nmax; j+=ninc) {
  1172.       if ( (float)j * ppyunit >= ymin &&  (float)j * ppyunit <= ymax ) {
  1173.     if (drawGrid) {
  1174.       PSsetlinewidth([gridThicknessText floatValue]);
  1175.       PSmoveto(xmin, (float)j * ppyunit);
  1176.       if ([gridDotted state])
  1177.         PSsetdash(pattern2, 2, 0.0);
  1178.       else
  1179.         PSsetdash(pattern0, 0, 0.0);
  1180.       PSrlineto(ABS(xmax-xmin), 0.0);
  1181.       PSstroke();
  1182.       PSsetlinewidth([ticMarkThicknessText floatValue]);
  1183.     }
  1184.     PSmoveto(xmin - ticmarklen*6.0, (float)j * ppyunit); /* big tic mark */
  1185.     PSsetdash(pattern0, 0, 0.0);
  1186.     PSrlineto(ticmarklen*6.0, 0.0);
  1187.     PSstroke();
  1188.         if (ticLocation == 2) {    /* tics on right and top */
  1189.       PSmoveto(xmax, (float)j * ppyunit); /* big tic mark */
  1190.       PSsetdash(pattern0, 0, 0.0);
  1191.       PSrlineto(ticmarklen*6.0, 0.0);
  1192.       PSstroke();
  1193.         }
  1194.     if ([handFormatYaxis state] == 0) {
  1195.       ticloc_rat = yhgt/DEFAULTFONTSIZE;
  1196.       PSmoveto(xmin - MAX(40.0*ticloc_rat,
  1197.                   40.0*ticloc_rat*(ticmarklen+10.0)/10.0),
  1198.            (float)j * ppyunit - ticloc_rat*7.0);
  1199.       PSshow("10");
  1200.       [ticfont1 set];
  1201.       PSmoveto(xmin - MAX(24.0*ticloc_rat,
  1202.                    4.0*ticloc_rat*(ticmarklen + 6.0)),
  1203.            (float)j * ppyunit - ticloc_rat*1.0);
  1204.       sprintf(ticlabel, "%-5d", j);
  1205.       PSshow(ticlabel);
  1206.       [ticfont set];
  1207.     }
  1208.     else {
  1209.       y = (float) pow((double)10.0, (double)j);
  1210.       handformat(y, ticlabel, axformat);
  1211.       xwid = [ticfont getWidthOf:ticlabel];
  1212.       PSmoveto(xmin - xwid - MAX(10.0, 5.0*ticmarklen),
  1213.                (float)j * ppyunit + 2.0 - yhgt/2.0);
  1214.       PSshow(ticlabel);
  1215.     }
  1216.       }
  1217.       if (drawMinorTics) {
  1218.     for (i=2; i<=9; i++) {
  1219.       ticloc = (float)j * ppyunit + ppyunit*(float)log10((double)i);
  1220.       if (ticloc>ymin && ticloc<ymax) {
  1221.         PSmoveto(xmin - ticmarklen*3.0, ticloc); /* small tic mark */
  1222.         PSrlineto(ticmarklen*3.0, 0.0);
  1223.         PSstroke();
  1224.             if (ticLocation == 2) { /* tics on right and top */
  1225.           PSmoveto(xmax, ticloc); /* small tic mark */
  1226.           PSrlineto(ticmarklen*3.0, 0.0);
  1227.           PSstroke();
  1228.             }
  1229.       }
  1230.     }
  1231.       }
  1232.     }
  1233.   }
  1234.   else {            /* y-axis is linear */
  1235.     xticloc = (ticLocation > 0 ? xmin : 2.0*ticmarklen) ;
  1236.     /* If inc is big, skip tic marks entirely */
  1237.     if (fabs(yinc_unscaled) < fabs(ymax_unscaled - ymin_unscaled)) {
  1238.       count_labels(&nlabels, &first, ymin_unscaled, yinc_unscaled, ymax_unscaled);
  1239.       if ([handFormatYaxis state] == 1) {
  1240.     axformat[0] = [yFormatLeft intValue];
  1241.     axformat[1] = [yFormatRight intValue];
  1242.     axformat[2] = [yFormatExponent intValue];
  1243.       }
  1244.       else {
  1245.     autoformat(ymin_unscaled, yinc_unscaled, ymax_unscaled, axformat);
  1246.     [yFormatLeft setIntValue:axformat[0]];
  1247.     [yFormatRight setIntValue:axformat[1]];
  1248.     [yFormatExponent setIntValue:axformat[2]];
  1249.       }
  1250.       /*
  1251.        * next loop starts at -1 because there may be room for minor tic
  1252.        * marks to the left of the first major tic mark (after a zoom, e.g.)
  1253.        */
  1254.       for (i = -1; i < nlabels; i++) {
  1255.     /* Special test here for what should be exact 0 (but isn't sometimes
  1256.      * due to floating-point arithmetic.
  1257.      */
  1258.     if (fabs(first/yinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
  1259.       y = 0.0;
  1260.     }
  1261.     else {
  1262.       y = (first + (yinc_unscaled)*(float)i) * ppyunit;
  1263.     }
  1264.     if (y >= ymin) {    /* ensure major tic mark won't be off edge */
  1265.       if (drawGrid) {
  1266.         PSsetlinewidth([gridThicknessText floatValue]);
  1267.         PSmoveto(xmin, y);
  1268.         if ([gridDotted state])
  1269.           PSsetdash(pattern2, 2, 0.0);
  1270.         else
  1271.           PSsetdash(pattern0, 0, 0.0);
  1272.         PSrlineto(ABS(xmax-xmin), 0.0);
  1273.         PSstroke();
  1274.         PSsetlinewidth([ticMarkThicknessText floatValue]);
  1275.       }
  1276.       /* Nothing at 0 if we're putting tic marks on axes:  */
  1277.       if (ticLocation > 0 || y != 0.0) {
  1278.         PSmoveto(xticloc - ticmarklen*4.0, y);
  1279.         PSsetdash(pattern0, 0, 0.0);
  1280.         PSrlineto(ticmarklen*4.0, 0.0);
  1281.         PSstroke();
  1282.             if (ticLocation == 2) { /* tics on right and top */
  1283.               PSmoveto(xmax, y);
  1284.           PSsetdash(pattern0, 0, 0.0);
  1285.           PSrlineto(ticmarklen*4.0, 0.0);
  1286.           PSstroke();
  1287.             }
  1288.         handformat(y/ppyunit, ticlabel, axformat);
  1289.         xwid = [ticfont getWidthOf:ticlabel];
  1290.         PSmoveto(xticloc - xwid - MAX(10.0, 5.0*ticmarklen),
  1291.              y + 2.0 - yhgt/2.0);
  1292.         PSshow(ticlabel);
  1293.       }
  1294.     }
  1295.     if (drawMinorTics) {
  1296.           if (ticLocation > 0) { /* tics on frame */
  1297.         for (j=1; j<=9; j++) {
  1298.           ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
  1299.           if (ticloc>ymin && ticloc<ymax) {
  1300.             PSmoveto(xticloc - ticmarklen*2.0, ticloc);
  1301.             PSrlineto(ticmarklen*2.0, 0.0);
  1302.             PSstroke();
  1303.                 if (ticLocation == 2) {    /* tics on right and top */
  1304.               PSmoveto(xmax, ticloc);
  1305.               PSrlineto(ticmarklen*2.0, 0.0);
  1306.               PSstroke();
  1307.                 }
  1308.               }
  1309.         }
  1310.       }
  1311.           else {
  1312.             for (j=1; j<=9; j++) {
  1313.           ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
  1314.           if (ticloc>ymin && ticloc<ymax) {
  1315.             PSmoveto(-ticmarklen, ticloc);
  1316.             PSrlineto(2.0*ticmarklen, 0.0);
  1317.             PSstroke();
  1318.               }
  1319.         }
  1320.       }
  1321.     }
  1322.       }
  1323.     }
  1324.   }
  1325.   return self;
  1326. }
  1327.  
  1328. - drawSelf: (const NXRect *)rects :(int)rectCount
  1329. {
  1330.   float  xmin = (float)[plotParam provideXmin];
  1331.   float  xmax = (float)[plotParam provideXmax];
  1332.   float  ymin = (float)[plotParam provideYmin];
  1333.   float  ymax = (float)[plotParam provideYmax];
  1334.  
  1335.   [self startPlot];
  1336.  
  1337.   if ([plotParam nFiles] == 0) return self; /* no data */
  1338.  
  1339.   if (xmin==xmax || ymin==ymax) return self; /* avoid division by zero */
  1340.  
  1341.   PSgsave();
  1342.  
  1343.   PStranslate(XOFFSET, YOFFSET);
  1344.  
  1345.   if ([plotParam xaxisLog]) {    /* x-axis is logarithmic */
  1346.     ppxunit = 0.9*(bounds.size.width-XOFFSET)
  1347.       /(float)log10((double)(xmax/xmin));
  1348.     xmin = (float)log10((double)xmin) * ppxunit;
  1349.     xmax = (float)log10((double)xmax) * ppxunit;
  1350.   }
  1351.   else {            /* x-axis is linear */
  1352.     ppxunit = 0.9*(bounds.size.width-XOFFSET)/(xmax-xmin);
  1353.     xmin = xmin*ppxunit;    /* drawing is all in pixel coordinates */
  1354.     xmax = xmax*ppxunit;
  1355.   }
  1356.   if ([plotParam yaxisLog]) {    /* y-axis is logarithmic */
  1357.     ppyunit = 0.9*(bounds.size.height-YOFFSET)
  1358.       /(float)log10((double)(ymax/ymin));
  1359.     ymin = (float)log10((double)ymin) * ppyunit;
  1360.     ymax = (float)log10((double)ymax) * ppyunit;
  1361.   }
  1362.   else {            /* y-axis is linear */
  1363.     ppyunit = 0.9*(bounds.size.height-YOFFSET)/(ymax-ymin);
  1364.     ymin = ymin*ppyunit;
  1365.     ymax = ymax*ppyunit;
  1366.   }
  1367.   PStranslate(-xmin, -ymin);
  1368.   // inner "frame" box
  1369.   if ([frameBoxOnOff state]) {
  1370.     PSsetlinewidth([frameBoxThicknessText floatValue]);
  1371.     PSnewpath();
  1372.     PSmoveto(xmin, ymin);
  1373.     PSlineto(xmax, ymin);
  1374.     PSlineto(xmax, ymax);
  1375.     PSlineto(xmin, ymax);
  1376.     PSclosepath();
  1377.     PSstroke();
  1378.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0: 0.4); /* reset line width */
  1379.   }
  1380.  
  1381.   if ([axesOnOff state]) {
  1382.     PSnewpath();
  1383.     PSsetlinewidth([axisThicknessText floatValue]);
  1384.     PSmoveto(0.0, ymin);
  1385.     PSlineto(0.0, ymax);
  1386.     PSstroke();
  1387.     PSmoveto(xmin, 0.0);
  1388.     PSlineto(xmax, 0.0);
  1389.     PSstroke();
  1390.   }
  1391.  
  1392.   if ([majorTicMarksOnOff state])
  1393.     [self drawTicMarks:xmin :xmax :ymin :ymax]; /* do this before clipping */
  1394.  
  1395.   /* MIN and MAX below in case xmin > xmax  and/or  ymin > ymax. */
  1396.   PSrectclip(MIN(xmin,xmax), MIN(ymin,ymax), ABS(xmax-xmin), ABS(ymax-ymin));
  1397.  
  1398.   [self drawLines:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1399.   [self drawSymbols:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1400.  
  1401.   [self drawErrorBars :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1402.  
  1403.   PSgrestore();
  1404.   if ([legendOnOff state]) {
  1405.     [self startLegend];
  1406.     [self drawLegend:self];
  1407.   }
  1408.  
  1409.   return self;
  1410. }
  1411.  
  1412. - doPrinting:sender
  1413. {
  1414.  
  1415.   id myPrintPanel = [PrintPanel new];
  1416.  
  1417.   [ [NXApp printInfo] setMarginLeft:72.0 right:72.0 top:72.0 bottom:72.0 ];
  1418.  
  1419.   if (bounds.size.height > bounds.size.width) { /* portrait mode */
  1420.     if (bounds.size.height/bounds.size.width > 9.0/6.5) {
  1421.       [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.height];
  1422.     }
  1423.     else {
  1424.       [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.width];
  1425.     }
  1426.     [ [NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES ];
  1427.   }
  1428.   else {            /* landscape mode */
  1429.     if (bounds.size.width/bounds.size.height > 9.0/6.5) {
  1430.       [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.width];
  1431.     }
  1432.     else {
  1433.       [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.height];
  1434.     }
  1435.     [ [NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES ];
  1436.   }
  1437.  
  1438.   [myPrintPanel setAccessoryView:printPanelAccessory];
  1439.  
  1440.   [self printPSCode:sender];
  1441.   return self;
  1442. }
  1443.  
  1444. - mouseDown:(NXEvent *)e
  1445. /*
  1446.  * This code taken from the Mandelbrot example in /NextDeveloper (and modified).
  1447.  * We implement the mouseDown method so the user can sweep out a section of
  1448.  * the view and select that as the current window in which to view the curve(s).
  1449.  */
  1450. {
  1451.   int looping = YES;
  1452.   int oldMask;
  1453.   NXRect bbox;
  1454.   NXPoint startPoint, currPoint;
  1455.   float xmin, xmax, ymin, ymax;
  1456.   register float t1, t2, t3, t4;
  1457.   int  zooming;
  1458.   BOOL xaxislog = [plotParam xaxisLog];
  1459.   BOOL yaxislog = [plotParam yaxisLog];
  1460.   
  1461.   zooming = ZOOM;
  1462.   if (strncmp([zoomChoice title], "Zoom/Move", 9) == 0)
  1463.     zooming = NOZOOM;
  1464.   else if (strncmp([zoomChoice title], "Move legend", 11) == 0)
  1465.     zooming = MOVELEGEND;
  1466.   else if (strncmp([zoomChoice title], "Move x title", 12) == 0)
  1467.     zooming = MOVEXTITLE;
  1468.   else if (strncmp([zoomChoice title], "Move y title", 12) == 0)
  1469.     zooming = MOVEYTITLE;
  1470.   else if (strncmp([zoomChoice title], "Move main title", 15) == 0)
  1471.     zooming = MOVEMAINTITLE;
  1472.  
  1473.   if (zooming == ZOOM) {    /* really zooming */
  1474.     if (xaxislog) {
  1475.       xmin = (float)log10([plotParam provideXmin]) * ppxunit;
  1476.       xmax = (float)log10([plotParam provideXmax]) * ppxunit;
  1477.     }
  1478.     else {
  1479.       xmin = [plotParam provideXmin] * ppxunit;
  1480.       xmax = [plotParam provideXmax] * ppxunit;
  1481.     }
  1482.     if (yaxislog) {
  1483.       ymin = (float)log10([plotParam provideYmin]) * ppyunit;
  1484.       ymax = (float)log10([plotParam provideYmax]) * ppyunit;
  1485.     }
  1486.     else {
  1487.       ymin = [plotParam provideYmin] * ppyunit;
  1488.       ymax = [plotParam provideYmax] * ppyunit;
  1489.     }
  1490.   
  1491.     oldMask =  [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  1492.     startPoint = e->location;
  1493.     [self convertPoint:&startPoint fromView:nil];
  1494.     NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0);
  1495.     [self lockFocus];
  1496.     while (looping) {
  1497.       e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  1498.       currPoint = e->location;
  1499.       [self convertPoint:&currPoint fromView:nil];
  1500.       bbox.size.width = (currPoint.x - startPoint.x);
  1501.       bbox.size.height = (currPoint.y - startPoint.y);
  1502.       /* Normalize bbox to always have positive width and height */
  1503.       if (bbox.size.width < 0) {
  1504.     bbox.size.width = -bbox.size.width;
  1505.     bbox.origin.x   = startPoint.x - bbox.size.width;
  1506.       }
  1507.       if (bbox.size.height < 0) {
  1508.     bbox.size.height = -bbox.size.height;
  1509.     bbox.origin.y    = startPoint.y - bbox.size.height;
  1510.       }
  1511.  
  1512.       PSnewinstance();
  1513.       if (looping = (e->type == NX_MOUSEDRAGGED)) {
  1514.     PSsetinstance(YES);
  1515.     NXSetColor([plotParam provideTextColor]);
  1516.     NXFrameRect(&bbox);
  1517.     PSsetinstance(NO);
  1518.       }
  1519.     }
  1520.     [self unlockFocus];
  1521.     [window setEventMask:oldMask];
  1522.     if ((bbox.size.width > 0) && (bbox.size.height > 0)) {
  1523.       /* At this point, bbox is in window coordinates.  Convert to curve
  1524.        * coordinates based on this view's coordinates and the bounding box.
  1525.        */
  1526.  
  1527.       xmin = xmin + (bbox.origin.x - XOFFSET);
  1528.       xmax = xmin + bbox.size.width;
  1529.       ymin = ymin + (bbox.origin.y - YOFFSET);
  1530.       ymax = ymin + bbox.size.height;
  1531.  
  1532.       /* save old min/max -- must adjust if log axis */
  1533.       t1 = xmin/ppxunit;
  1534.       t2 = xmax/ppxunit;
  1535.       t3 = ymin/ppyunit;
  1536.       t4 = ymax/ppyunit;
  1537.       if (xaxislog) {
  1538.     t1 = (float) pow((double)10.0, (double)t1);
  1539.     t2 = (float) pow((double)10.0, (double)t2);
  1540.       }
  1541.       if (yaxislog) {
  1542.     t3 = (float) pow((double)10.0, (double)t3);
  1543.     t4 = (float) pow((double)10.0, (double)t4);
  1544.       }
  1545.       [plotParam stackOldMinMax:t1 :t2 :t3 :t4];
  1546.  
  1547.       if (xaxislog) {
  1548.     [plotParam resetXmin:pow((double)10.0, (double)(xmin/ppxunit))];
  1549.     [plotParam resetXmax:pow((double)10.0, (double)(xmax/ppxunit))];
  1550.       }
  1551.       else {
  1552.     [plotParam resetXmin:(double)(xmin/ppxunit)];
  1553.     [plotParam resetXmax:(double)(xmax/ppxunit)];
  1554.       }
  1555.       if (yaxislog) {
  1556.     [plotParam resetYmin:pow((double)10.0, (double)(ymin/ppyunit))];
  1557.     [plotParam resetYmax:pow((double)10.0, (double)(ymax/ppyunit))];
  1558.       }
  1559.       else {
  1560.     [plotParam resetYmin:(double)(ymin/ppyunit)];
  1561.     [plotParam resetYmax:(double)(ymax/ppyunit)];
  1562.       }
  1563.  
  1564.       /*  Call [self display] to force current values of xmin/xmax/ymin/ymax
  1565.        *  to take effect (otherwise xmin, etc., get incremented too many times).
  1566.        */
  1567.       [plotParam drawPlotButton:1];
  1568.       [self display];
  1569.       [plotParam drawPlotButton:0];
  1570.     }
  1571.   }
  1572.   else if (zooming >= MOVELEGEND) {
  1573.  
  1574.     oldMask =  [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  1575.     startPoint = e->location;
  1576.     [self convertPoint:&startPoint fromView:nil];
  1577.     if (zooming == MOVELEGEND) {
  1578.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1579.         legendbox.size.width,legendbox.size.height);
  1580.     }
  1581.     else if (zooming == MOVEXTITLE) {
  1582.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1583.         xtitlebox.size.width,xtitlebox.size.height);
  1584.     }
  1585.     else if (zooming == MOVEYTITLE) {
  1586.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1587.         ytitlebox.size.width,ytitlebox.size.height);
  1588.     }
  1589.     else if (zooming == MOVEMAINTITLE) {
  1590.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1591.         maintitlebox.size.width,maintitlebox.size.height);
  1592.     }
  1593.     [self lockFocus];
  1594.     while (looping) {
  1595.       e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  1596.       currPoint = e->location;
  1597.       [self convertPoint:&currPoint fromView:nil];
  1598.       bbox.origin.x = currPoint.x;
  1599.       bbox.origin.y = currPoint.y;
  1600.       PSnewinstance();
  1601.       if (looping = (e->type == NX_MOUSEDRAGGED)) {
  1602.     PSsetinstance(YES);
  1603.     NXSetColor([plotParam provideTextColor]);
  1604.     NXFrameRect(&bbox);
  1605.     PSsetinstance(NO);
  1606.       }
  1607.     }
  1608.     [self unlockFocus];
  1609.     [window setEventMask:oldMask];
  1610.     /*
  1611.      * At this point, bbox is in window coordinates. 
  1612.      */
  1613.     if (zooming == MOVELEGEND) {
  1614.       legendbox.origin.x = bbox.origin.x;
  1615.       legendbox.origin.y = bbox.origin.y;
  1616.     }
  1617.     else if (zooming == MOVEXTITLE) {
  1618.       xtitlebox.origin.x = bbox.origin.x;
  1619.       xtitlebox.origin.y = bbox.origin.y;
  1620.     }
  1621.     else if (zooming == MOVEYTITLE) {
  1622.       ytitlebox.origin.x = bbox.origin.x;
  1623.       ytitlebox.origin.y = bbox.origin.y;
  1624.     }
  1625.     else if (zooming == MOVEMAINTITLE) {
  1626.       maintitlebox.origin.x = bbox.origin.x;
  1627.       maintitlebox.origin.y = bbox.origin.y;
  1628.     }
  1629.  
  1630.     /*  Call [self display] to give immediate feedback   */
  1631.     [plotParam drawPlotButton:1];
  1632.     [self display];
  1633.     [plotParam drawPlotButton:0];
  1634.   }
  1635.  
  1636.   return self;
  1637. }
  1638.  
  1639. - saveEPS:sender
  1640. {
  1641.   id savePanel = [[SavePanel new] setRequiredFileType:"eps"];
  1642.   char  *eps_fileName;    // Name of the EPS file for output
  1643.  
  1644.   [savePanel setTitle:"Save EPS"]; /* make sure title is OK */
  1645.   if ([savePanel runModal])  {
  1646.     eps_fileName = (char *) calloc(strlen([savePanel filename])+1, sizeof(char));
  1647.     strcpy(eps_fileName, [savePanel filename]);
  1648.     [self savePSCode:eps_fileName];
  1649.   }
  1650.  
  1651.   return self;
  1652. }
  1653.  
  1654. - savePSCode:(char *)aFile
  1655. {
  1656.     NXStream    *psStream;
  1657.  
  1658.     psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1659.     if (!psStream)  {
  1660.       return self;
  1661.     }
  1662.     [self getBounds:&bounds];
  1663.     [self copyPSCodeInside:&bounds to:psStream];
  1664.        
  1665.     NXFlush(psStream);
  1666.     NXSaveToFile(psStream, aFile);
  1667.     NXCloseMemory(psStream, NX_FREEBUFFER);
  1668.  
  1669.     return self;
  1670. }
  1671.  
  1672. /*
  1673.  * Following code taken from the Graph example in NextDeveloper/Examples.
  1674.  *
  1675.  * Copies the current view into the Pasteboard as PostScript.
  1676.  */
  1677. - copyPScode:sender
  1678. {
  1679.     NXStream *psStream;
  1680.     id        pb;
  1681.     char     *data;
  1682.     int       dataLen, maxDataLen;
  1683.  
  1684.   /* Open a stream on memory where we will collect the PostScript */
  1685.     psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1686.     if (!psStream)
  1687.       return self;
  1688.  
  1689.   /* Tell the Pasteboard we're going to copy PostScript */
  1690.     pb = [Pasteboard new];
  1691.     [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
  1692.  
  1693.   /* writes the PostScript for the whole plot as EPS into the stream */
  1694.     [self getBounds:&bounds];
  1695.     [self copyPSCodeInside:&bounds to:psStream];
  1696.  
  1697.   /* get the buffered up PostScript out of the stream */
  1698.     NXGetMemoryBuffer(psStream, &data, &dataLen, &maxDataLen);
  1699.  
  1700.   /* put the buffer in the Pasteboard, free the stream (and the buffer) */
  1701.     [pb writeType:NXPostScriptPboardType data:data length:dataLen];
  1702.     NXCloseMemory(psStream, NX_FREEBUFFER);
  1703.     return self;
  1704. }
  1705.  
  1706. @end
  1707.